iT邦幫忙

2023 iThome 鐵人賽

DAY 21
0
SideProject30

30 天用 Rust 打造 QR Code 製造機系列 第 21

Day 21 - 將表單重構成元件(上)

  • 分享至 

  • xImage
  •  

現在 page.tsx 的程式碼已經有點多了,而且之後還要新增一個產生 PNG 的表單,所以現在就把原本產生 SVG 的表單重構成元件,這樣之後要新增表單的時候,就可以直接在 page.tsx 中引入元件,而不是直接在 page.tsx 中寫表單。

新增 CreateSvgForm 元件

先在 app/components 資料夾下新增一個 CreateSvgForm.tsx 檔案,並且把原本 page.tsx 中的所有有關表單的程式碼複製到 CreateSvgForm.tsx 中:

import { useEffect } from 'react'
import { useForm, SubmitHandler } from 'react-hook-form'
import generateQrcode from '../lib/api/generateQrcode'
import { TextInput, SelectType, SizeSlider, ColorPicker } from '../components'
import useStore from '../store'

export type FormInputs = {
  text: string
  qrType: string
  qrSize: number
  qrColor: string
  qrBgColor: string
}

type QrCodeData = {
  url?: string
  phone?: string
  address?: string
  email?: string
  foreground: string
  background: string
  dimensions: number
}

const CreateSvgForm = () => {
  const { setImgSrc } = useStore()
  const {
    register,
    handleSubmit,
    setValue,
    formState: { errors },
    watch
  } = useForm<FormInputs>({
    defaultValues: {
      qrSize: 500,
      qrColor: '#000000',
      qrBgColor: '#ffffff'
    }
  })

  const qrType = watch('qrType', 'URL')

  useEffect(() => {
    setImgSrc(null)
  }, [qrType])

  const validationPatterns = {
    URL: /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/,
    電話: /^(\+?\d{1,3}[-.\s]?)?\d{10}$/,
    地址: /.+/,
    Email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  }

  const pattern = validationPatterns[qrType as keyof typeof validationPatterns]

  const fetchQrcodeSvg = async (formData: FormInputs) => {
    try {
      const typeMapping: { [key in FormInputs['qrType']]: keyof QrCodeData } = {
        URL: 'url',
        電話: 'phone',
        地址: 'address',
        Email: 'email'
      }

      const dataKey = typeMapping[formData.qrType]
      const data: QrCodeData = {
        [dataKey]: formData.text,
        foreground: formData.qrColor,
        background: formData.qrBgColor,
        dimensions: formData.qrSize
      }
      const response = await generateQrcode.getSvg(data)
      const blob = new Blob([response.data], { type: 'image/svg+xml' })
      const objectURL = URL.createObjectURL(blob)
      setImgSrc(objectURL)
    } catch (_) {
      console.error('Error fetching image:')
    }
  }

  const onSubmit: SubmitHandler<FormInputs> = async (data) => {
    await fetchQrcodeSvg(data)
  }

  const hasErrors = Object.keys(errors).length > 0

  return (
    <form onSubmit={handleSubmit(onSubmit)} className='flex flex-col gap-y-5'>
      <SelectType register={register} />
      <TextInput register={register} pattern={pattern} errors={errors} />
      <div className='flex items-center justify-evenly'>
        <SizeSlider register={register} setValue={setValue} />
        <ColorPicker
          register={register}
          label='選擇顏色'
          name='qrColor'
          setValue={setValue}
        />
        <ColorPicker
          register={register}
          label='選擇背景顏色'
          name='qrBgColor'
          setValue={setValue}
        />
      </div>
      <div className='flex justify-center mt-5'>
        <button
          type='submit'
          className={`${
            hasErrors ? 'bg-gray-400' : 'bg-green-500'
          } hover:bg-green-100 text-white hover:text-slate-700 font-bold py-2 px-4 rounded text-center`}
        >
          產生 QR Code
        </button>
      </div>
    </form>
  )
}

export default CreateSvgForm

在程式碼搬家的過程中,要記得原本的一些路徑可能會有變動,所以要記得修改路徑。總之,現在 page.tsx 中的程式碼就可以簡化成這樣:

'use client'
import Image from 'next/image'
import useStore from './store'
import CreateSvgForm from './components/CreateSvgForm'

export default function Home() {
  const { imgSrc } = useStore()

  const hasErrors = Object.keys(errors).length > 0

  return (
    <main className='flex min-h-screen flex-col items-center justify-between p-24'>
      <div className='container mx-auto p-4'>
        <h1 className='text-3xl mb-4'>QR Code 製造器</h1>
        <CreateSvgForm />
        {imgSrc && Object.keys(errors).length < 1 ? (
          <div className='flex justify-center mt-10'>
            <Image src={imgSrc} width={500} height={500} alt='QR Code Image' />
          </div>
        ) : (
          <p>{hasErrors ? '請輸入正確的資訊' : '請點擊「產生 QR Code」按鈕'}</p>
        )}
      </div>
    </main>
  )
}

不過,眼尖的你可能會發現,現在因為原本從 useForm 中取出的 errors 也一起搬到 CreateSvgForm.tsx 中,所以現在 page.tsx 中的 hasErrors 變數會報錯,因為 errors 變數不存在了。

那麼,要怎麼解決這個問題呢?其實很簡單,只要把 errors 也從 CreateSvgForm.tsx 中傳到 page.tsx 中就可以了。

因為本篇的篇幅已經有點長,所以就到下一篇文章再做介紹囉!


上一篇
Day 20 - 使用 Zustand 來做狀態管理
下一篇
Day 22 - 將表單重構成元件(下)
系列文
30 天用 Rust 打造 QR Code 製造機30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言